package com.clickhouse.jdbc.internal;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.clickhouse.client.ClickHouseRequest;
import com.clickhouse.data.ClickHouseUtils;
import com.clickhouse.jdbc.SqlExceptionUtils;

public abstract class AbstractPreparedStatement extends ClickHouseStatementImpl implements PreparedStatement {
    protected AbstractPreparedStatement(ClickHouseConnectionImpl connection, ClickHouseRequest<?> request,
            int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        super(connection, request, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    protected abstract long[] executeAny(boolean asBatch) throws SQLException;

    protected abstract int getMaxParameterIndex();

    protected int toArrayIndex(int parameterIndex) throws SQLException {
        int max = getMaxParameterIndex();
        if (max < 1) {
            String name = getConnection().getJdbcConfig().useNamedParameter() ? "named parameter"
                    : "JDBC style '?' placeholder";
            throw SqlExceptionUtils.clientError(ClickHouseUtils
                    .format("Can't set parameter at index %d due to no %s found in the query",
                            parameterIndex, name));
        } else if (parameterIndex < 1 || parameterIndex > max) {
            throw SqlExceptionUtils.clientError(ClickHouseUtils
                    .format("Parameter index must between 1 and %d but we got %d", max,
                            parameterIndex));
        }

        return parameterIndex - 1;
    }

    @Override
    public final void addBatch(String sql) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "addBatch(String) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final boolean execute(String sql) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "execute(String) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "execute(String, int) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final boolean execute(String sql, int[] columnIndexes) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "execute(String, int[]) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final boolean execute(String sql, String[] columnNames) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "execute(String, String[]) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public long[] executeLargeBatch() throws SQLException {
        return executeAny(true);
    }

    @Override
    public final long executeLargeUpdate(String sql) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "executeLargeUpdate(String) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "executeLargeUpdate(String, int) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "executeLargeUpdate(String, int[]) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final long executeLargeUpdate(String sql, String[] columnNames) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "executeLargeUpdate(String, String[]) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final ResultSet executeQuery(String sql) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "executeQuery(String) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final int executeUpdate() throws SQLException {
        return (int) executeLargeUpdate();
    }

    @Override
    public final int executeUpdate(String sql) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "executeUpate(String) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "executeUpdate(String, int) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "executeUpdate(String, int[]) cannot be called in PreparedStatement or CallableStatement!");
    }

    @Override
    public final int executeUpdate(String sql, String[] columnNames) throws SQLException {
        ensureOpen();

        throw SqlExceptionUtils
                .unsupportedError(
                        "executeUpdate(String, String[]) cannot be called in PreparedStatement or CallableStatement!");
    }
}
